import time

from flask import Flask, render_template, request,session,redirect
import json
from . import prestock
import os
from frameworks.cores.Config import *
from frameworks.utils.JsonResult2 import *
from frameworks.cores.LoadClass import *
import akshare as ak
from flask import Flask, render_template
from pyecharts.charts import Line
from pyecharts import options as opts
import pandas as pd

basepath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
config = Config(basepath)

apimap = config.getApi()
env = apimap["base"]
print(env)
from flask_session import Session

jsonResult = JsonResult2()

loader = LoadClass()

import datetime
import time

modeldir = config.getAiModel()[0]

# 确保模型目录存在
if not os.path.exists(modeldir):
    print("警告1: 未找到预训练模型，实际应用中需要先训练模型")

import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
import matplotlib
# 关键修改：强制使用Agg后端并禁用GUI相关功能
matplotlib.use('Agg')
# 禁用Matplotlib的GUI相关功能
matplotlib.rcParams['backend'] = 'Agg'
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import io
import base64
import os
from flask import Flask, render_template, request, jsonify

# 设置中文字体
plt.rcParams["font.family"] = ["SimSun", "SimHei"]
plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题

app = Flask(__name__)

import sys

# 导入实际的股票数据服务
sys.path.append("../../")  # 确保能找到服务路径
from services.DayKlineService import *
from model.StockBaseInfo import *

# 初始化实际服务
base = StockBaseInfo()
klineService = DayKlineService()


def load_and_preprocess_data(code):
    """加载并预处理股票数据（使用实际DayKlineService）"""
    try:
        sinacode = base.translateSinaCode(code).upper()
        data = klineService.getAllDataArr(sinacode)
        if not data:
            return False

        stock_data = pd.DataFrame(data, columns=[
            'id', 'code', 'Date', 'Open', 'High', 'Low', 'Close', 'Volume',
            'Money', 'ma2', 'ma5', 'ma10', 'ma20', 'ma30', 'ma60', 'pre_close', 'pre_money'
        ])

        # 将Date列转换为datetime类型
        stock_data['Date'] = pd.to_datetime(stock_data['Date'])

        # 排序并设置日期索引
        dfall = stock_data.sort_values(by="Date")
        dfall.set_index('Date', inplace=True)
        return dfall
    except Exception as e:
        print(f"数据加载错误: {e}")
        return False


def create_dataset(data, look_back=30):
    """创建用于LSTM的数据集"""
    if data is False or len(data) < look_back + 1:
        return np.array([]), np.array([])

    scaled_data = MinMaxScaler(feature_range=(0, 1)).fit_transform(data[['Close']])
    X, Y = [], []
    for i in range(len(scaled_data) - look_back):
        X.append(scaled_data[i:(i + look_back), 0])
        Y.append(scaled_data[i + look_back, 0])
    return np.array(X), np.array(Y)


def build_model(input_shape):
    """构建LSTM预测模型"""
    inputs = tf.keras.layers.Input(shape=input_shape)
    lstm_out = tf.keras.layers.LSTM(50, return_sequences=True)(inputs)
    attention_out = tf.keras.layers.Attention()([lstm_out, lstm_out])
    dropout_out = tf.keras.layers.TimeDistributed(tf.keras.layers.Dropout(0.2))(attention_out)
    outputs = tf.keras.layers.TimeDistributed(tf.keras.layers.Dense(1))(dropout_out)

    model = tf.keras.models.Model(inputs=inputs, outputs=outputs)
    model.compile(optimizer='adam', loss='mean_squared_error')
    return model


def predict_future(model, scaler, data, look_back=30, days=5):
    """预测未来n天的股价"""
    if data is False or len(data) < look_back:
        return np.array([])

    scaled_data = scaler.transform(data)
    last_sequence = scaled_data[-look_back:].reshape(1, look_back, 1)
    future_predictions = []

    for _ in range(days):
        next_day = model.predict(last_sequence, verbose=0)
        predicted_value = next_day[0, -1, 0]
        future_predictions.append(predicted_value)
        last_sequence = np.roll(last_sequence, -1, axis=1)
        last_sequence[0, -1, 0] = predicted_value

    future_predictions = np.array(future_predictions).reshape(-1, 1)
    future_predictions = scaler.inverse_transform(future_predictions)
    return future_predictions


def generate_prediction_chart(data, future_predictions, look_back=30):
    """生成预测图表并返回Base64编码"""
    if data is False or len(data) < look_back:
        return None

    plt.figure(figsize=(12, 6))

    # 绘制实际历史股价
    plt.plot(data.index, data['Close'], label='实际股价', color='blue')

    # 准备历史预测数据
    scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_data = scaler.fit_transform(data[['Close']])

    # 创建历史预测数据
    X, _ = create_dataset(data, look_back)

    if len(X) > 0:
        # 重塑数据以适应LSTM模型
        X = np.reshape(X, (X.shape[0], X.shape[1], 1))

        # 加载或创建模型
        if not os.path.exists(modeldir):
            model = build_model((look_back, 1))
            # 注意：实际应用中需要训练模型
        else:
            model = tf.keras.models.load_model(modeldir)

        # 预测历史数据
        historical_predictions = model.predict(X, verbose=0)
        historical_predictions = historical_predictions[:, -1, 0].reshape(-1, 1)
        historical_predictions = scaler.inverse_transform(historical_predictions)

        # 创建历史预测的日期索引
        hist_dates = data.index[look_back:]
        plt.plot(hist_dates, historical_predictions, label='历史预测', color='green', alpha=0.7)

    # 绘制未来预测线
    last_date = data.index[-1]
    future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=len(future_predictions))
    plt.plot(future_dates, future_predictions, 'r--o', label='未来预测')

    # 连接最后一个实际点和第一个未来预测点
    if len(data) > 0 and len(future_predictions) > 0:
        plt.plot(
            [data.index[-1], future_dates[0]],
            [data['Close'].iloc[-1], future_predictions[0, 0]],
            'k--', alpha=0.5
        )

    plt.title('股票价格预测与历史表现')
    plt.xlabel('日期')
    plt.ylabel('价格')
    plt.legend()
    plt.grid(True)
    plt.tight_layout()

    # 将图表转换为Base64编码
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    image_base64 = base64.b64encode(buf.read()).decode('utf-8')
    plt.close()

    return image_base64


@prestock.route('/', methods=['GET', 'POST'])
def index():
    """首页路由，处理表单提交和结果展示"""
    if request.method == 'POST':
        code = request.form.get('code')
        if not code:
            return render_template('front/prestock.html', error="请输入股票代码")

        # 加载实际数据
        data = load_and_preprocess_data(code)
        if data is False:
            return render_template('front/prestock.html', error="无法加载股票数据，请检查代码是否正确或服务是否可用")

        # 数据标准化
        scaler = MinMaxScaler(feature_range=(0, 1))
        scaled_data = scaler.fit_transform(data[['Close']])

        # 预测未来5天股价
        look_back = 30
        if not os.path.exists(modeldir):
            return render_template('front/prestock.html', error="未找到预训练模型，请先训练模型")

        model = tf.keras.models.load_model(modeldir)
        future_predictions = predict_future(model, scaler, data[['Close']], look_back, days=5)

        if len(future_predictions) == 0:
            return render_template('front/prestock.html', error="数据不足，无法进行预测")

        # 生成图表
        chart_image = generate_prediction_chart(data, future_predictions, look_back)
        if not chart_image:
            return render_template('front/prestock.html', error="图表生成失败")

        # 确保last_price是float类型
        last_price = float(data['Close'].iloc[-1]) if len(data) > 0 else 0.0

        # 准备预测结果（全部存储为float类型）
        results = []
        last_date = data.index[-1] if len(data) > 0 else datetime.now()
        # 在生成预测结果时添加类型检查
        for price in future_predictions:
            try:
                price_value = float(price[0])
            except (ValueError, TypeError):
                price_value = 0.0  # 处理异常情况
            future_date = last_date + timedelta(days=len(results) + 1)
            results.append({
                'date': future_date.strftime('%Y-%m-%d'),
                'price': price_value
            })

        return render_template(
            'front/result.html',
            code=code,
            results=results,
            chart_image=chart_image,
            last_price=last_price
        )
    return render_template('front/prestock.html')


@prestock.route('/api/predict', methods=['GET'])
def api_predict():
    """API接口，返回预测数据"""
    code = request.args.get('code')
    if not code:
        return jsonify({"error": "缺少股票代码参数"}), 400

    data = load_and_preprocess_data(code)
    if data is False:
        return jsonify({"error": "无法加载股票数据"}), 404

    scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_data = scaler.fit_transform(data[['Close']])

    look_back = 30
    if not os.path.exists(modeldir):
        return jsonify({"error": "未找到预训练模型"}), 500

    model = tf.keras.models.load_model(modeldir)
    future_predictions = predict_future(model, scaler, data[['Close']], look_back, days=5)

    if len(future_predictions) == 0:
        return jsonify({"error": "数据不足，无法预测"}), 400

    results = []
    last_date = data.index[-1]
    for i, price in enumerate(future_predictions, 1):
        future_date = last_date + timedelta(days=i)
        results.append({
            'date': future_date.strftime('%Y-%m-%d'),
            'price': float(price[0]),
            'code': code
        })

    return jsonify({
        "code": code,
        "last_price": float(data['Close'].iloc[-1]),
        "predictions": results
    })


